package wordfilter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.markdown4j.Markdown4jProcessor;
import sardine.Route;
import sardine.SardineException;
import sardine.log.Logs;
import wordfilter.dfa.Sensitives;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Stream;

import static sardine.Sardine.*;

/**
 * @auth bruce-sha
 * @date 2015/6/23
 */
public class Main {

    static String APP_HOME;

    public static void main(String[] args) {

//        System.setProperty("app.home", "D:/buru/gitosc/wordfilter");// debug模式

        config();

        Logs.info(() -> "configuration initialized.");

        final String host = System.getProperty("sardine.host", "127.0.0.1");
        host(host);

        final String port = System.getProperty("sardine.port", "9119");
        port(Integer.parseInt(port));

        Logs.info(() -> "server started on " + host + ":" + port);

        final String dicHome = System.getProperty("dic.home", APP_HOME + "/config/dics");
        final Sensitives sens = Sensitives.singleton(dicHome);

        Logs.info(() -> "sensitive words dictionary loaded from " + dicHome);

        final String[] apps = System.getProperty("security.apps", "").split(",");

        Logs.info(() -> "security apps: " + apps);

        final Route wordFilterRoute = (request, response) -> {

            response.contentType("application/json");

            final String body = request.body();
            JSONObject bodyObject = JSON.parseObject(body);

            String action = bodyObject.getString("action");
            String level = bodyObject.getString("level");
            String data = bodyObject.getString("data");

            if ("replace".equalsIgnoreCase(action)) {
                Optional<String> mosaic = sens.mosaic(level, data);
                if (!mosaic.isPresent()) return new WordFilterResponseVO(true);
                else return new WordFilterResponseVO(mosaic.get());
            } else if ("verify".equalsIgnoreCase(action)) {
                Set<String> sensitives = sens.judge(level, data);
                if (sensitives.isEmpty()) return new WordFilterResponseVO(true);
                else return new WordFilterResponseVO(sensitives);
            } else {
                throw new SardineException("unknown action");
            }
        };

        post("/security", (request, response) -> {
            final long start = System.currentTimeMillis();

            String appId = request.queryParams("appId");
            String subject = request.queryParams("subject");

            final ResponseVO re = new ResponseVO();
            if (Stream.of(apps).parallel().noneMatch(e -> e.equals(appId)))
                return re.fail().result("illegal app");

            if ("word_filter".equalsIgnoreCase(subject)) {
                try {
                    re.result(wordFilterRoute.apply(request, response));
                } catch (Exception e) {
                    re.fail().result(e.getMessage());
                }
            } else {
                re.fail().result("unknown subject");
            }

            Logs.info(() -> "APP:" + appId + ",CONSUME:" + request.body() +
                    ",PRODUCE:" + JSON.toJSONString(re) + ",COST:" + (System.currentTimeMillis() - start) + "ms.");

            return re;
        }, (o) -> JSON.toJSONString(o));

        get("/", () -> "i got u! @" + LocalDateTime.now());

        get("/wiki", () -> {
            try {
                Path readmePath = Paths.get(APP_HOME + "/config/README.md");
                return new Markdown4jProcessor().process(Files.newInputStream(readmePath));
            } catch (IOException e) {
                Logs.error("read wiki file error:", e);
                return "can not find wiki file.";
            }
        });
    }

    // 输入：/security?appId=""&subject=
    // {action:'',level:''，data:''}
    static void config() {
        try {
            APP_HOME = System.getProperty("app.home");
            System.setProperty("logback.configurationFile", APP_HOME + "/config/logback.xml");

            Logs.info(() -> "java started from directory: " + System.getProperty("user.dir"));
            Logs.info(() -> "APP_HOME directory: " + APP_HOME);
            Logs.info(() -> "logback configurationFile: " + System.getProperty("logback.configurationFile"));

            final Properties props = new Properties();
            props.load(Files.newInputStream(Paths.get(APP_HOME + "/config/wordfilter.properties")));
            props.forEach((k, v) -> System.setProperty(String.valueOf(k), String.valueOf(v)));

            Logs.info(() -> "properties: " + props);

        } catch (IOException e) {
            Logs.error("config error: ", e);
        }
    }

    public static class ResponseVO<T> {
        boolean success = true;
        T result;

        ResponseVO fail() {
            this.success = false;
            return this;
        }

        ResponseVO result(T result) {
            this.result = result;
            return this;
        }

        public boolean isSuccess() {
            return success;
        }

        public T getResult() {
            return result;
        }
    }

    /**
     * {"legalContent":true, "illegalWords":[]}
     * {"legalContent":false,"illegalWords":["职业报仇","pcp气枪网","枪","气枪"]}
     */
    public static class WordFilterResponseVO {
        Set<String> illegalWords;
        String legalContent;
        boolean isLegal;

        WordFilterResponseVO(Set<String> illegalWords) {
            this.illegalWords = illegalWords;
        }

        WordFilterResponseVO(String legalContent) {
            this.legalContent = legalContent;
        }

        WordFilterResponseVO(boolean isLegal) {
            this.isLegal = isLegal;
        }

        public Boolean isLegal() {
            return isLegal;
        }

        public Set<String> getIllegalWords() {
            return illegalWords;
        }

        public String getLegalContent() {
            return legalContent;
        }
    }

}